home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2006 May / PCWMAY06.iso / Software / Freeware / First Page 2006 3.00 / fp2006-final-3.00-setup.exe / {app} / Iscripts / Forms Misc / val-universal.izs < prev    next >
Text File  |  2005-09-28  |  32KB  |  1,122 lines

  1. <!NOWIZARD>
  2.  
  3. <!TITLE>Validation (Universal Date)
  4. <!/TITLE>
  5.  
  6. <!DESCRIPTION> Validate dates in your form using this universal script. Will work with any date format and the code is fully commented for easy modification. <!/DESCRIPTION> 
  7.  
  8. <!CATEGORY>Forms<!/CATEGORY>
  9.  
  10. <!SCRIPT>
  11. <!-- START OF SCRIPT -->
  12.  
  13. <!-- HOW TO INSTALL VALIDATION (UNIVERSAL DATE):
  14.  
  15.   1.  Copy code into the HEAD section of document
  16.   2.  Put last coding into the BODY section of document  -->
  17.  
  18. <!-- STEP ONE: Add code into HEAD section of document  -->
  19.  
  20. <HEAD>
  21.  
  22. <SCRIPT LANGUAGE="JavaScript">
  23. <!-- Original:  Sandeep Tamhankar (stamhankar@hotmail.com) -->
  24.  
  25.  
  26.  
  27. <!-- Begin
  28. /* Here's the list of tokens we support:
  29.    m (or M) : month number, one or two digits.
  30.    mm (or MM) : month number, strictly two digits (i.e. April is 04).
  31.    d (or D) : day number, one or two digits.
  32.    dd (or DD) : day number, strictly two digits.
  33.    y (or Y) : year, two or four digits.
  34.    yy (or YY) : year, strictly two digits.
  35.    yyyy (or YYYY) : year, strictly four digits.
  36.    mon : abbreviated month name (April is apr, Apr, APR, etc.)
  37.    Mon : abbreviated month name, mixed-case (i.e. April is Apr only).
  38.    MON : abbreviated month name, all upper-case (i.e. April is APR only).
  39.    mon_strict : abbreviated month name, all lower-case (i.e. April is apr 
  40.          only).
  41.    month : full month name (April is april, April, APRIL, etc.)
  42.    Month : full month name, mixed-case (i.e. April only).
  43.    MONTH: full month name, all upper-case (i.e. APRIL only).
  44.    month_strict : full month name, all lower-case (i.e. april only).
  45.    h (or H) : hour, one or two digits.
  46.    hh (or HH) : hour, strictly two digits.
  47.    min (or MIN): minutes, one or two digits.
  48.    mins (or MINS) : minutes, strictly two digits.
  49.    s (or S) : seconds, one or two digits.
  50.    ss (or SS) : seconds, strictly two digits.
  51.    ampm (or AMPM) : am/pm setting.  Valid values to match this token are
  52.          am, pm, AM, PM, a.m., p.m., A.M., P.M.
  53. */
  54. // Be careful with this pattern.  Longer tokens should be placed before shorter
  55. // tokens to disambiguate them.  For example, parsing "mon_strict" should 
  56. // result in one token "mon_strict" and not two tokens "mon" and a literal
  57. // "_strict".
  58.  
  59. var tokPat=new RegExp("^month_strict|month|Month|MONTH|yyyy|YYYY|mins|MINS|mon_strict|ampm|AMPM|mon|Mon|MON|min|MIN|dd|DD|mm|MM|yy|YY|hh|HH|ss|SS|m|M|d|D|y|Y|h|H|s|S");
  60.  
  61. // lowerMonArr is used to map months to their numeric values.
  62.  
  63. var lowerMonArr={jan:1, feb:2, mar:3, apr:4, may:5, jun:6, jul:7, aug:8, sep:9, oct:10, nov:11, dec:12}
  64.  
  65. // monPatArr contains regular expressions used for matching abbreviated months
  66. // in a date string.
  67.  
  68. var monPatArr=new Array();
  69. monPatArr['mon_strict']=new RegExp(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/);
  70. monPatArr['Mon']=new RegExp(/Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec/);
  71. monPatArr['MON']=new RegExp(/JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC/);
  72. monPatArr['mon']=new RegExp("jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec",'i');
  73.  
  74. // monthPatArr contains regular expressions used for matching full months
  75. // in a date string.
  76.  
  77. var monthPatArr=new Array();
  78. monthPatArr['month']=new RegExp(/^january|february|march|april|may|june|july|august|september|october|november|december/i);
  79. monthPatArr['Month']=new RegExp(/^January|February|March|April|May|June|July|August|September|October|November|December/);
  80. monthPatArr['MONTH']=new RegExp(/^JANUARY|FEBRUARY|MARCH|APRIL|MAY|JUNE|JULY|AUGUST|SEPTEMBER|OCTOBER|NOVEMBER|DECEMBER/);
  81. monthPatArr['month_strict']=new RegExp(/^january|february|march|april|may|june|july|august|september|october|november|december/);
  82.  
  83. // cutoffYear is the cut-off for assigning "19" or "20" as century.  Any
  84. // two-digit year >= cutoffYear will get a century of "19", and everything
  85. // else gets a century of "20".
  86.  
  87. var cutoffYear=50;
  88.  
  89. // FormatToken is a datatype we use for storing extracted tokens from the
  90. // format string.
  91.  
  92. function FormatToken (token, type) {
  93. this.token=token;
  94. this.type=type;
  95. }
  96.  
  97. function parseFormatString (formatStr) {
  98. var tokArr=new Array;
  99. var tokInd=0;
  100. var strInd=0;
  101. var foundTok=0;
  102.     
  103. while (strInd < formatStr.length) {
  104. if (formatStr.charAt(strInd)=="%" &&
  105. (matchArray=formatStr.substr(strInd+1).match(tokPat)) != null) {
  106. strInd+=matchArray[0].length+1;
  107. tokArr[tokInd++]=new FormatToken(matchArray[0],"symbolic");
  108. } else {
  109.  
  110. // No token matched current position, so current character should 
  111. // be saved as a required literal.
  112.  
  113. if (tokInd>0 && tokArr[tokInd-1].type=="literal") {
  114.  
  115. // Literal tokens can be combined.Just add to the last token.
  116.  
  117. tokArr[tokInd-1].token+=formatStr.charAt(strInd++);
  118. }
  119. else {
  120. tokArr[tokInd++]=new FormatToken(formatStr.charAt(strInd++), "literal");
  121.       }
  122.    }
  123. }
  124. return tokArr;
  125. }
  126.  
  127. /* buildDate does all the real work.It takes a date string and format string,
  128.  tries to match the two up, and returns a Date object (with the supplied date
  129.  string value).If a date string doesn't contain all the fields that a Date
  130.  object contains (for example, a date string with just the month), all
  131.  unprovided fields are defaulted to those characteristics of the current
  132.  date. Time fields that aren't provided default to 0.Thus, a date string
  133.  like "3/30/2000" in "%mm/%dd/%yyyy" format results in a Date object for that
  134.  date at midnight.formatStr is a free-form string that indicates special
  135.  tokens via the % character.Here are some examples that will return a Date
  136.  object:
  137.  
  138.  buildDate('3/30/2000','%mm/%dd/%y') // March 30, 2000
  139.  buildDate('March 30, 2000','%Mon %d, %y') // Same as above.
  140.  buildDate('Here is the date: 30-3-00','Here is the date: %dd-%m-%yy')
  141.  
  142.  If the format string does not match the string provided, an error message
  143.  (i.e. String object) is returned.Thus, to see if buildDate succeeded, the
  144.  caller can use the "typeof" command on the return value.For example,
  145.  here's the dateCheck function, which returns true if a given date is
  146.  valid,and false otherwise (and reports an error in the false case):
  147.  
  148.  function dateCheck(dateStr,formatStr) {
  149.  var myObj=buildDate(dateStr,formatStr);
  150.  if (typeof myObj=="object") {
  151.  // We got a Date object, so good.
  152.  return true;
  153.  } else {
  154.  // We got an error string.
  155.  alert(myObj);
  156.  return false;
  157.  }
  158.  }
  159.  
  160. */
  161.  
  162. function buildDate(dateStr,formatStr) {
  163. // parse the format string first.
  164. var tokArr=parseFormatString(formatStr);
  165. var strInd=0;
  166. var tokInd=0;
  167. var intMonth;
  168. var intDay;
  169. var intYear;
  170. var intHour;
  171. var intMin;
  172. var intSec;
  173. var ampm="";
  174. var strOffset;
  175.  
  176. // Create a date object with the current date so that if the user only
  177. // gives a month or day string, we can still return a valid date.
  178.  
  179. var curdate=new Date();
  180. intMonth=curdate.getMonth()+1;
  181. intDay=curdate.getDate();
  182. intYear=curdate.getFullYear();
  183.  
  184. // Default time to midnight, so that if given just date info, we return
  185. // a Date object for that date at midnight.
  186.  
  187. intHour=0;
  188. intMin=0;
  189. intSec=0;
  190.  
  191. // Walk across dateStr, matching the parsed formatStr until we find a 
  192. // mismatch or succeed.
  193.  
  194. while (strInd < dateStr.length && tokInd < tokArr.length) {
  195.  
  196. // Start with the easy case of matching a literal.
  197.  
  198. if (tokArr[tokInd].type=="literal") {
  199. if (dateStr.indexOf(tokArr[tokInd].token,strInd)==strInd) {
  200.  
  201. // The current position in the string does match the format 
  202. // pattern.
  203.  
  204. strInd+=tokArr[tokInd++].token.length;
  205. continue;
  206. }
  207. else {
  208.  
  209. // ACK! There was a mismatch; return error.
  210.  
  211. return "\"" + dateStr + "\" does not conform to the expected format: " + formatStr;
  212.    }
  213. }
  214.  
  215. // If we get here, we're matching to a symbolic token.
  216. switch (tokArr[tokInd].token) {
  217. case 'm':
  218. case 'M':
  219. case 'd':
  220. case 'D':
  221. case 'h':
  222. case 'H':
  223. case 'min':
  224. case 'MIN':
  225. case 's':
  226. case 'S':
  227.  
  228. // Extract one or two characters from the date-time string and if 
  229. // it's a number, save it as the month, day, hour, or minute, as
  230. // appropriate.
  231.  
  232. curChar=dateStr.charAt(strInd);
  233. nextChar=dateStr.charAt(strInd+1);
  234. matchArr=dateStr.substr(strInd).match(/^\d{1,2}/);
  235. if (matchArr==null) {
  236.  
  237. // First character isn't a number; there's a mismatch between
  238. // the pattern and date string, so return error.
  239.  
  240. switch (tokArr[tokInd].token.toLowerCase()) {
  241. case 'd': var unit="day"; break;
  242. case 'm': var unit="month"; break;
  243. case 'h': var unit="hour"; break;
  244. case 'min': var unit="minute"; break;
  245. case 's': var unit="second"; break;
  246. }
  247. return "Bad " + unit + " \"" + curChar + "\" or \"" + curChar +
  248. nextChar + "\".";
  249. }
  250. strOffset=matchArr[0].length;
  251. switch (tokArr[tokInd].token.toLowerCase()) {
  252. case 'd': intDay=parseInt(matchArr[0],10); break;
  253. case 'm': intMonth=parseInt(matchArr[0],10); break;
  254. case 'h': intHour=parseInt(matchArr[0],10); break;
  255. case 'min': intMin=parseInt(matchArr[0],10); break;
  256. case 's': intSec=parseInt(matchArr[0],10); break;
  257. }
  258. break;
  259. case 'mm':
  260. case 'MM':
  261. case 'dd':
  262. case 'DD':
  263. case 'hh':
  264. case 'HH':
  265. case 'mins':
  266. case 'MINS':
  267. case 'ss':
  268. case 'SS':
  269.  
  270. // Extract two characters from the date string and if it's a 
  271. // number, save it as the month, day, or hour, as appropriate.
  272.  
  273. strOffset=2;
  274. matchArr=dateStr.substr(strInd).match(/^\d{2}/);
  275. if (matchArr==null) {
  276.  
  277. // The two characters aren't a number; there's a mismatch 
  278. // between the pattern and date string, so return an error
  279. // message.
  280.  
  281. switch (tokArr[tokInd].token.toLowerCase()) {
  282. case 'dd': var unit="day"; break;
  283. case 'mm': var unit="month"; break;
  284. case 'hh': var unit="hour"; break;
  285. case 'mins': var unit="minute"; break;
  286. case 'ss': var unit="second"; break;
  287. }
  288. return "Bad " + unit + " \"" + dateStr.substr(strInd,2) + 
  289. "\".";
  290. }
  291. switch (tokArr[tokInd].token.toLowerCase()) {
  292. case 'dd': intDay=parseInt(matchArr[0],10); break;
  293. case 'mm': intMonth=parseInt(matchArr[0],10); break;
  294. case 'hh': intHour=parseInt(matchArr[0],10); break;
  295. case 'mins': intMin=parseInt(matchArr[0],10); break;
  296. case 'ss': intSec=parseInt(matchArr[0],10); break;
  297. }
  298. break;
  299. case 'y':
  300. case 'Y':
  301.  
  302. // Extract two or four characters from the date string and if it's
  303. // a number, save it as the year.Convert two-digit years to four
  304. // digit years by assigning a century of '19' if the year is >= 
  305. // cutoffYear, and '20' otherwise.
  306.  
  307. if (dateStr.substr(strInd,4).search(/\d{4}/) != -1) {
  308.  
  309. // Four digit year.
  310.  
  311. intYear=parseInt(dateStr.substr(strInd,4),10);
  312. strOffset=4;
  313. }
  314. else {
  315. if (dateStr.substr(strInd,2).search(/\d{2}/) != -1) {
  316.  
  317. // Two digit year.
  318.  
  319. intYear=parseInt(dateStr.substr(strInd,2),10);
  320. if (intYear>=cutoffYear) {
  321. intYear+=1900;
  322. }
  323. else {
  324. intYear+=2000;
  325. }
  326. strOffset=2;
  327. }
  328. else {
  329.  
  330. // Bad year; return error.
  331.  
  332. return "Bad year \"" + dateStr.substr(strInd,2) + 
  333. "\". Must be two or four digits.";
  334.    }
  335. }
  336. break;
  337. case 'yy':
  338. case 'YY':
  339.  
  340. // Extract two characters from the date string and if it's a 
  341. // number, save it as the year.Convert two-digit years to four 
  342. // digit years by assigning a century of '19' if the year is >= 
  343. // cutoffYear, and '20' otherwise.
  344.  
  345. if (dateStr.substr(strInd,2).search(/\d{2}/) != -1) {
  346.  
  347. // Two digit year.
  348.  
  349. intYear=parseInt(dateStr.substr(strInd,2),10);
  350. if (intYear>=cutoffYear) {
  351. intYear+=1900;
  352. }
  353. else {
  354. intYear+=2000;
  355. }
  356. strOffset=2;
  357. } else {
  358. // Bad year; return error
  359. return "Bad year \"" + dateStr.substr(strInd,2) + 
  360. "\". Must be two digits.";
  361. }
  362. break;
  363. case 'yyyy':
  364. case 'YYYY':
  365.  
  366. // Extract four characters from the date string and if it's a 
  367. // number, save it as the year.
  368.  
  369. if (dateStr.substr(strInd,4).search(/\d{4}/) != -1) {
  370.  
  371. // Four digit year.
  372.  
  373. intYear=parseInt(dateStr.substr(strInd,4),10);
  374. strOffset=4;
  375. }
  376. else {
  377.  
  378. // Bad year; return error.
  379.  
  380. return "Bad year \"" + dateStr.substr(strInd,4) + 
  381. "\". Must be four digits.";
  382. }
  383. break;
  384. case 'mon':
  385. case 'Mon':
  386. case 'MON':
  387. case 'mon_strict':
  388.  
  389. // Extract three characters from dateStr and parse them as 
  390. // lower-case, mixed-case, or upper-case abbreviated months,
  391. // as appropriate.
  392.  
  393. monPat=monPatArr[tokArr[tokInd].token];
  394. if (dateStr.substr(strInd,3).search(monPat) != -1) {
  395. intMonth=lowerMonArr[dateStr.substr(strInd,3).toLowerCase()];
  396. }
  397. else {
  398.  
  399. // Bad month, return error.
  400.  
  401. switch (tokArr[tokInd].token) {
  402. case 'mon_strict': caseStat="lower-case"; break;
  403. case 'Mon': caseStat="mixed-case"; break;
  404. case 'MON': caseStat="upper-case"; break;
  405. case 'mon': caseStat="between Jan and Dec"; break;
  406. }
  407. return "Bad month \"" + dateStr.substr(strInd,3) + 
  408. "\". Must be " + caseStat + ".";
  409. }
  410. strOffset=3;
  411. break;
  412. case 'month':
  413. case 'Month':
  414. case 'MONTH':
  415. case 'month_strict':
  416.  
  417. // Extract a full month name at strInd from dateStr if possible.
  418.  
  419. monPat=monthPatArr[tokArr[tokInd].token];
  420. matchArray=dateStr.substr(strInd).match(monPat);
  421. if (matchArray==null) {
  422.  
  423. // Bad month, return error.
  424.  
  425. return "Can't find a month beginning at \"" +
  426. dateStr.substr(strInd) + "\".";
  427. }
  428.  
  429. // It's a good month.
  430.  
  431. intMonth=lowerMonArr[matchArray[0].substr(0,3).toLowerCase()];
  432. strOffset=matchArray[0].length;
  433. break;
  434. case 'ampm':
  435. case 'AMPM':
  436. matchArr=dateStr.substr(strInd).match(/^(am|pm|AM|PM|a\.m\.|p\.m\.|A\.M\.|P\.M\.)/);
  437. if (matchArr==null) {
  438.  
  439. // There's no am/pm in the string.Return error msg.
  440.  
  441. return "Missing am/pm designation.";
  442. }
  443.  
  444. // Store am/pm value for later (as just am or pm, to make things
  445. // easier later).
  446.  
  447. if (matchArr[0].substr(0,1).toLowerCase() == "a") {
  448.  
  449. // This is am.
  450.  
  451. ampm = "am";
  452. }
  453. else {
  454. ampm = "pm";
  455. }
  456. strOffset = matchArr[0].length;
  457. break;
  458. }
  459. strInd += strOffset;
  460. tokInd++;
  461. }
  462. if (tokInd != tokArr.length || strInd != dateStr.length) {
  463.  
  464. /* We got through the whole date string or format string, but there's 
  465.  more data in the other, so there's a mismatch. */
  466.  
  467. return "\"" + dateStr + "\" is either missing desired information or has more information than the expected format: " + formatStr;
  468. }
  469.  
  470. // Make sure all components are in the right ranges.
  471.  
  472. if (intMonth < 1 || intMonth > 12) {
  473. return "Month must be between 1 and 12.";
  474. }
  475. if (intDay < 1 || intDay > 31) {
  476. return "Day must be between 1 and 31.";
  477. }
  478.  
  479. // Make sure user doesn't put 31 for a month that only has 30 days
  480.  
  481. if ((intMonth == 4 || intMonth == 6 || intMonth == 9 || intMonth == 11) && intDay == 31) {
  482. return "Month "+intMonth+" doesn't have 31 days!";
  483. }
  484.  
  485. // Check for February date validity (including leap years) 
  486.  
  487. if (intMonth == 2) {
  488.  
  489. // figure out if "year" is a leap year; don't forget that
  490. // century years are only leap years if divisible by 400
  491.  
  492. var isleap=(intYear%4==0 && (intYear%100!=0 || intYear%400==0));
  493. if (intDay > 29 || (intDay == 29 && !isleap)) {
  494. return "February " + intYear + " doesn't have " + intDay + 
  495. " days!";
  496.    }
  497. }
  498.  
  499. // Check that if am/pm is not provided, hours are between 0 and 23.
  500.  
  501. if (ampm == "") {
  502. if (intHour < 0 || intHour > 23) {
  503. return "Hour must be between 0 and 23 for military time.";
  504.    }
  505. }
  506. else {
  507.  
  508. // non-military time, so make sure it's between 1 and 12.
  509.  
  510. if (intHour < 1|| intHour > 12) {
  511. return "Hour must be between 1 and 12 for standard time.";
  512.    }
  513. }
  514.  
  515. // If user specified amor pm, convert intHour to military.
  516.  
  517. if (ampm=="am" && intHour==12) {
  518. intHour=0;
  519. }
  520. if (ampm=="pm" && intHour < 12) {
  521. intHour += 12;
  522. }
  523. if (intMin < 0 || intMin > 59) {
  524. return "Minute must be between 0 and 59.";
  525. }
  526. if (intSec < 0 || intSec > 59) {
  527. return "Second must be between 0 and 59.";
  528. }
  529. return new Date(intYear,intMonth-1,intDay,intHour,intMin,intSec);
  530. }
  531. function dateCheck(dateStr,formatStr) {
  532. var myObj = buildDate(dateStr,formatStr);
  533. if (typeof myObj == "object") {
  534.  
  535. // We got a Date object, so good.
  536.  
  537. return true;
  538. }
  539. else {
  540.  
  541. // We got an error string.
  542.  
  543. alert(myObj);
  544. return false;
  545.    }
  546. }
  547. //  End -->
  548. </script>
  549.  
  550. </HEAD>
  551.  
  552. <!-- STEP TWO: Add code into BODY section of document  -->
  553.  
  554. <BODY>
  555.  
  556. <form>
  557. <input type=text name="email">
  558. <input type=button value="Check it!" onClick="if(dateCheck(this.form.email.value,'%mm/%dd/%y')) {alert('Valid Date!')}">
  559. </form>
  560.  
  561.  
  562.  
  563. <!-- END OF SCRIPT -->
  564. <!/SCRIPT>
  565.  
  566. <!PREVIEW>
  567. <!-- START OF SCRIPT -->
  568.  
  569. <!-- HOW TO INSTALL VALIDATION (UNIVERSAL DATE):
  570.  
  571.   1.  Copy code into the HEAD section of document
  572.   2.  Put last coding into the BODY section of document  -->
  573.  
  574. <!-- STEP ONE: Add code into HEAD section of document  -->
  575.  
  576. <HEAD>
  577.  
  578. <SCRIPT LANGUAGE="JavaScript">
  579. <!-- Original:  Sandeep Tamhankar (stamhankar@hotmail.com) -->
  580.  
  581.  
  582.  
  583. <!-- Begin
  584. /* Here's the list of tokens we support:
  585.    m (or M) : month number, one or two digits.
  586.    mm (or MM) : month number, strictly two digits (i.e. April is 04).
  587.    d (or D) : day number, one or two digits.
  588.    dd (or DD) : day number, strictly two digits.
  589.    y (or Y) : year, two or four digits.
  590.    yy (or YY) : year, strictly two digits.
  591.    yyyy (or YYYY) : year, strictly four digits.
  592.    mon : abbreviated month name (April is apr, Apr, APR, etc.)
  593.    Mon : abbreviated month name, mixed-case (i.e. April is Apr only).
  594.    MON : abbreviated month name, all upper-case (i.e. April is APR only).
  595.    mon_strict : abbreviated month name, all lower-case (i.e. April is apr 
  596.          only).
  597.    month : full month name (April is april, April, APRIL, etc.)
  598.    Month : full month name, mixed-case (i.e. April only).
  599.    MONTH: full month name, all upper-case (i.e. APRIL only).
  600.    month_strict : full month name, all lower-case (i.e. april only).
  601.    h (or H) : hour, one or two digits.
  602.    hh (or HH) : hour, strictly two digits.
  603.    min (or MIN): minutes, one or two digits.
  604.    mins (or MINS) : minutes, strictly two digits.
  605.    s (or S) : seconds, one or two digits.
  606.    ss (or SS) : seconds, strictly two digits.
  607.    ampm (or AMPM) : am/pm setting.  Valid values to match this token are
  608.          am, pm, AM, PM, a.m., p.m., A.M., P.M.
  609. */
  610. // Be careful with this pattern.  Longer tokens should be placed before shorter
  611. // tokens to disambiguate them.  For example, parsing "mon_strict" should 
  612. // result in one token "mon_strict" and not two tokens "mon" and a literal
  613. // "_strict".
  614.  
  615. var tokPat=new RegExp("^month_strict|month|Month|MONTH|yyyy|YYYY|mins|MINS|mon_strict|ampm|AMPM|mon|Mon|MON|min|MIN|dd|DD|mm|MM|yy|YY|hh|HH|ss|SS|m|M|d|D|y|Y|h|H|s|S");
  616.  
  617. // lowerMonArr is used to map months to their numeric values.
  618.  
  619. var lowerMonArr={jan:1, feb:2, mar:3, apr:4, may:5, jun:6, jul:7, aug:8, sep:9, oct:10, nov:11, dec:12}
  620.  
  621. // monPatArr contains regular expressions used for matching abbreviated months
  622. // in a date string.
  623.  
  624. var monPatArr=new Array();
  625. monPatArr['mon_strict']=new RegExp(/jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/);
  626. monPatArr['Mon']=new RegExp(/Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec/);
  627. monPatArr['MON']=new RegExp(/JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC/);
  628. monPatArr['mon']=new RegExp("jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec",'i');
  629.  
  630. // monthPatArr contains regular expressions used for matching full months
  631. // in a date string.
  632.  
  633. var monthPatArr=new Array();
  634. monthPatArr['month']=new RegExp(/^january|february|march|april|may|june|july|august|september|october|november|december/i);
  635. monthPatArr['Month']=new RegExp(/^January|February|March|April|May|June|July|August|September|October|November|December/);
  636. monthPatArr['MONTH']=new RegExp(/^JANUARY|FEBRUARY|MARCH|APRIL|MAY|JUNE|JULY|AUGUST|SEPTEMBER|OCTOBER|NOVEMBER|DECEMBER/);
  637. monthPatArr['month_strict']=new RegExp(/^january|february|march|april|may|june|july|august|september|october|november|december/);
  638.  
  639. // cutoffYear is the cut-off for assigning "19" or "20" as century.  Any
  640. // two-digit year >= cutoffYear will get a century of "19", and everything
  641. // else gets a century of "20".
  642.  
  643. var cutoffYear=50;
  644.  
  645. // FormatToken is a datatype we use for storing extracted tokens from the
  646. // format string.
  647.  
  648. function FormatToken (token, type) {
  649. this.token=token;
  650. this.type=type;
  651. }
  652.  
  653. function parseFormatString (formatStr) {
  654. var tokArr=new Array;
  655. var tokInd=0;
  656. var strInd=0;
  657. var foundTok=0;
  658.     
  659. while (strInd < formatStr.length) {
  660. if (formatStr.charAt(strInd)=="%" &&
  661. (matchArray=formatStr.substr(strInd+1).match(tokPat)) != null) {
  662. strInd+=matchArray[0].length+1;
  663. tokArr[tokInd++]=new FormatToken(matchArray[0],"symbolic");
  664. } else {
  665.  
  666. // No token matched current position, so current character should 
  667. // be saved as a required literal.
  668.  
  669. if (tokInd>0 && tokArr[tokInd-1].type=="literal") {
  670.  
  671. // Literal tokens can be combined.Just add to the last token.
  672.  
  673. tokArr[tokInd-1].token+=formatStr.charAt(strInd++);
  674. }
  675. else {
  676. tokArr[tokInd++]=new FormatToken(formatStr.charAt(strInd++), "literal");
  677.       }
  678.    }
  679. }
  680. return tokArr;
  681. }
  682.  
  683. /* buildDate does all the real work.It takes a date string and format string,
  684.  tries to match the two up, and returns a Date object (with the supplied date
  685.  string value).If a date string doesn't contain all the fields that a Date
  686.  object contains (for example, a date string with just the month), all
  687.  unprovided fields are defaulted to those characteristics of the current
  688.  date. Time fields that aren't provided default to 0.Thus, a date string
  689.  like "3/30/2000" in "%mm/%dd/%yyyy" format results in a Date object for that
  690.  date at midnight.formatStr is a free-form string that indicates special
  691.  tokens via the % character.Here are some examples that will return a Date
  692.  object:
  693.  
  694.  buildDate('3/30/2000','%mm/%dd/%y') // March 30, 2000
  695.  buildDate('March 30, 2000','%Mon %d, %y') // Same as above.
  696.  buildDate('Here is the date: 30-3-00','Here is the date: %dd-%m-%yy')
  697.  
  698.  If the format string does not match the string provided, an error message
  699.  (i.e. String object) is returned.Thus, to see if buildDate succeeded, the
  700.  caller can use the "typeof" command on the return value.For example,
  701.  here's the dateCheck function, which returns true if a given date is
  702.  valid,and false otherwise (and reports an error in the false case):
  703.  
  704.  function dateCheck(dateStr,formatStr) {
  705.  var myObj=buildDate(dateStr,formatStr);
  706.  if (typeof myObj=="object") {
  707.  // We got a Date object, so good.
  708.  return true;
  709.  } else {
  710.  // We got an error string.
  711.  alert(myObj);
  712.  return false;
  713.  }
  714.  }
  715.  
  716. */
  717.  
  718. function buildDate(dateStr,formatStr) {
  719. // parse the format string first.
  720. var tokArr=parseFormatString(formatStr);
  721. var strInd=0;
  722. var tokInd=0;
  723. var intMonth;
  724. var intDay;
  725. var intYear;
  726. var intHour;
  727. var intMin;
  728. var intSec;
  729. var ampm="";
  730. var strOffset;
  731.  
  732. // Create a date object with the current date so that if the user only
  733. // gives a month or day string, we can still return a valid date.
  734.  
  735. var curdate=new Date();
  736. intMonth=curdate.getMonth()+1;
  737. intDay=curdate.getDate();
  738. intYear=curdate.getFullYear();
  739.  
  740. // Default time to midnight, so that if given just date info, we return
  741. // a Date object for that date at midnight.
  742.  
  743. intHour=0;
  744. intMin=0;
  745. intSec=0;
  746.  
  747. // Walk across dateStr, matching the parsed formatStr until we find a 
  748. // mismatch or succeed.
  749.  
  750. while (strInd < dateStr.length && tokInd < tokArr.length) {
  751.  
  752. // Start with the easy case of matching a literal.
  753.  
  754. if (tokArr[tokInd].type=="literal") {
  755. if (dateStr.indexOf(tokArr[tokInd].token,strInd)==strInd) {
  756.  
  757. // The current position in the string does match the format 
  758. // pattern.
  759.  
  760. strInd+=tokArr[tokInd++].token.length;
  761. continue;
  762. }
  763. else {
  764.  
  765. // ACK! There was a mismatch; return error.
  766.  
  767. return "\"" + dateStr + "\" does not conform to the expected format: " + formatStr;
  768.    }
  769. }
  770.  
  771. // If we get here, we're matching to a symbolic token.
  772. switch (tokArr[tokInd].token) {
  773. case 'm':
  774. case 'M':
  775. case 'd':
  776. case 'D':
  777. case 'h':
  778. case 'H':
  779. case 'min':
  780. case 'MIN':
  781. case 's':
  782. case 'S':
  783.  
  784. // Extract one or two characters from the date-time string and if 
  785. // it's a number, save it as the month, day, hour, or minute, as
  786. // appropriate.
  787.  
  788. curChar=dateStr.charAt(strInd);
  789. nextChar=dateStr.charAt(strInd+1);
  790. matchArr=dateStr.substr(strInd).match(/^\d{1,2}/);
  791. if (matchArr==null) {
  792.  
  793. // First character isn't a number; there's a mismatch between
  794. // the pattern and date string, so return error.
  795.  
  796. switch (tokArr[tokInd].token.toLowerCase()) {
  797. case 'd': var unit="day"; break;
  798. case 'm': var unit="month"; break;
  799. case 'h': var unit="hour"; break;
  800. case 'min': var unit="minute"; break;
  801. case 's': var unit="second"; break;
  802. }
  803. return "Bad " + unit + " \"" + curChar + "\" or \"" + curChar +
  804. nextChar + "\".";
  805. }
  806. strOffset=matchArr[0].length;
  807. switch (tokArr[tokInd].token.toLowerCase()) {
  808. case 'd': intDay=parseInt(matchArr[0],10); break;
  809. case 'm': intMonth=parseInt(matchArr[0],10); break;
  810. case 'h': intHour=parseInt(matchArr[0],10); break;
  811. case 'min': intMin=parseInt(matchArr[0],10); break;
  812. case 's': intSec=parseInt(matchArr[0],10); break;
  813. }
  814. break;
  815. case 'mm':
  816. case 'MM':
  817. case 'dd':
  818. case 'DD':
  819. case 'hh':
  820. case 'HH':
  821. case 'mins':
  822. case 'MINS':
  823. case 'ss':
  824. case 'SS':
  825.  
  826. // Extract two characters from the date string and if it's a 
  827. // number, save it as the month, day, or hour, as appropriate.
  828.  
  829. strOffset=2;
  830. matchArr=dateStr.substr(strInd).match(/^\d{2}/);
  831. if (matchArr==null) {
  832.  
  833. // The two characters aren't a number; there's a mismatch 
  834. // between the pattern and date string, so return an error
  835. // message.
  836.  
  837. switch (tokArr[tokInd].token.toLowerCase()) {
  838. case 'dd': var unit="day"; break;
  839. case 'mm': var unit="month"; break;
  840. case 'hh': var unit="hour"; break;
  841. case 'mins': var unit="minute"; break;
  842. case 'ss': var unit="second"; break;
  843. }
  844. return "Bad " + unit + " \"" + dateStr.substr(strInd,2) + 
  845. "\".";
  846. }
  847. switch (tokArr[tokInd].token.toLowerCase()) {
  848. case 'dd': intDay=parseInt(matchArr[0],10); break;
  849. case 'mm': intMonth=parseInt(matchArr[0],10); break;
  850. case 'hh': intHour=parseInt(matchArr[0],10); break;
  851. case 'mins': intMin=parseInt(matchArr[0],10); break;
  852. case 'ss': intSec=parseInt(matchArr[0],10); break;
  853. }
  854. break;
  855. case 'y':
  856. case 'Y':
  857.  
  858. // Extract two or four characters from the date string and if it's
  859. // a number, save it as the year.Convert two-digit years to four
  860. // digit years by assigning a century of '19' if the year is >= 
  861. // cutoffYear, and '20' otherwise.
  862.  
  863. if (dateStr.substr(strInd,4).search(/\d{4}/) != -1) {
  864.  
  865. // Four digit year.
  866.  
  867. intYear=parseInt(dateStr.substr(strInd,4),10);
  868. strOffset=4;
  869. }
  870. else {
  871. if (dateStr.substr(strInd,2).search(/\d{2}/) != -1) {
  872.  
  873. // Two digit year.
  874.  
  875. intYear=parseInt(dateStr.substr(strInd,2),10);
  876. if (intYear>=cutoffYear) {
  877. intYear+=1900;
  878. }
  879. else {
  880. intYear+=2000;
  881. }
  882. strOffset=2;
  883. }
  884. else {
  885.  
  886. // Bad year; return error.
  887.  
  888. return "Bad year \"" + dateStr.substr(strInd,2) + 
  889. "\". Must be two or four digits.";
  890.    }
  891. }
  892. break;
  893. case 'yy':
  894. case 'YY':
  895.  
  896. // Extract two characters from the date string and if it's a 
  897. // number, save it as the year.Convert two-digit years to four 
  898. // digit years by assigning a century of '19' if the year is >= 
  899. // cutoffYear, and '20' otherwise.
  900.  
  901. if (dateStr.substr(strInd,2).search(/\d{2}/) != -1) {
  902.  
  903. // Two digit year.
  904.  
  905. intYear=parseInt(dateStr.substr(strInd,2),10);
  906. if (intYear>=cutoffYear) {
  907. intYear+=1900;
  908. }
  909. else {
  910. intYear+=2000;
  911. }
  912. strOffset=2;
  913. } else {
  914. // Bad year; return error
  915. return "Bad year \"" + dateStr.substr(strInd,2) + 
  916. "\". Must be two digits.";
  917. }
  918. break;
  919. case 'yyyy':
  920. case 'YYYY':
  921.  
  922. // Extract four characters from the date string and if it's a 
  923. // number, save it as the year.
  924.  
  925. if (dateStr.substr(strInd,4).search(/\d{4}/) != -1) {
  926.  
  927. // Four digit year.
  928.  
  929. intYear=parseInt(dateStr.substr(strInd,4),10);
  930. strOffset=4;
  931. }
  932. else {
  933.  
  934. // Bad year; return error.
  935.  
  936. return "Bad year \"" + dateStr.substr(strInd,4) + 
  937. "\". Must be four digits.";
  938. }
  939. break;
  940. case 'mon':
  941. case 'Mon':
  942. case 'MON':
  943. case 'mon_strict':
  944.  
  945. // Extract three characters from dateStr and parse them as 
  946. // lower-case, mixed-case, or upper-case abbreviated months,
  947. // as appropriate.
  948.  
  949. monPat=monPatArr[tokArr[tokInd].token];
  950. if (dateStr.substr(strInd,3).search(monPat) != -1) {
  951. intMonth=lowerMonArr[dateStr.substr(strInd,3).toLowerCase()];
  952. }
  953. else {
  954.  
  955. // Bad month, return error.
  956.  
  957. switch (tokArr[tokInd].token) {
  958. case 'mon_strict': caseStat="lower-case"; break;
  959. case 'Mon': caseStat="mixed-case"; break;
  960. case 'MON': caseStat="upper-case"; break;
  961. case 'mon': caseStat="between Jan and Dec"; break;
  962. }
  963. return "Bad month \"" + dateStr.substr(strInd,3) + 
  964. "\". Must be " + caseStat + ".";
  965. }
  966. strOffset=3;
  967. break;
  968. case 'month':
  969. case 'Month':
  970. case 'MONTH':
  971. case 'month_strict':
  972.  
  973. // Extract a full month name at strInd from dateStr if possible.
  974.  
  975. monPat=monthPatArr[tokArr[tokInd].token];
  976. matchArray=dateStr.substr(strInd).match(monPat);
  977. if (matchArray==null) {
  978.  
  979. // Bad month, return error.
  980.  
  981. return "Can't find a month beginning at \"" +
  982. dateStr.substr(strInd) + "\".";
  983. }
  984.  
  985. // It's a good month.
  986.  
  987. intMonth=lowerMonArr[matchArray[0].substr(0,3).toLowerCase()];
  988. strOffset=matchArray[0].length;
  989. break;
  990. case 'ampm':
  991. case 'AMPM':
  992. matchArr=dateStr.substr(strInd).match(/^(am|pm|AM|PM|a\.m\.|p\.m\.|A\.M\.|P\.M\.)/);
  993. if (matchArr==null) {
  994.  
  995. // There's no am/pm in the string.Return error msg.
  996.  
  997. return "Missing am/pm designation.";
  998. }
  999.  
  1000. // Store am/pm value for later (as just am or pm, to make things
  1001. // easier later).
  1002.  
  1003. if (matchArr[0].substr(0,1).toLowerCase() == "a") {
  1004.  
  1005. // This is am.
  1006.  
  1007. ampm = "am";
  1008. }
  1009. else {
  1010. ampm = "pm";
  1011. }
  1012. strOffset = matchArr[0].length;
  1013. break;
  1014. }
  1015. strInd += strOffset;
  1016. tokInd++;
  1017. }
  1018. if (tokInd != tokArr.length || strInd != dateStr.length) {
  1019.  
  1020. /* We got through the whole date string or format string, but there's 
  1021.  more data in the other, so there's a mismatch. */
  1022.  
  1023. return "\"" + dateStr + "\" is either missing desired information or has more information than the expected format: " + formatStr;
  1024. }
  1025.  
  1026. // Make sure all components are in the right ranges.
  1027.  
  1028. if (intMonth < 1 || intMonth > 12) {
  1029. return "Month must be between 1 and 12.";
  1030. }
  1031. if (intDay < 1 || intDay > 31) {
  1032. return "Day must be between 1 and 31.";
  1033. }
  1034.  
  1035. // Make sure user doesn't put 31 for a month that only has 30 days
  1036.  
  1037. if ((intMonth == 4 || intMonth == 6 || intMonth == 9 || intMonth == 11) && intDay == 31) {
  1038. return "Month "+intMonth+" doesn't have 31 days!";
  1039. }
  1040.  
  1041. // Check for February date validity (including leap years) 
  1042.  
  1043. if (intMonth == 2) {
  1044.  
  1045. // figure out if "year" is a leap year; don't forget that
  1046. // century years are only leap years if divisible by 400
  1047.  
  1048. var isleap=(intYear%4==0 && (intYear%100!=0 || intYear%400==0));
  1049. if (intDay > 29 || (intDay == 29 && !isleap)) {
  1050. return "February " + intYear + " doesn't have " + intDay + 
  1051. " days!";
  1052.    }
  1053. }
  1054.  
  1055. // Check that if am/pm is not provided, hours are between 0 and 23.
  1056.  
  1057. if (ampm == "") {
  1058. if (intHour < 0 || intHour > 23) {
  1059. return "Hour must be between 0 and 23 for military time.";
  1060.    }
  1061. }
  1062. else {
  1063.  
  1064. // non-military time, so make sure it's between 1 and 12.
  1065.  
  1066. if (intHour < 1|| intHour > 12) {
  1067. return "Hour must be between 1 and 12 for standard time.";
  1068.    }
  1069. }
  1070.  
  1071. // If user specified amor pm, convert intHour to military.
  1072.  
  1073. if (ampm=="am" && intHour==12) {
  1074. intHour=0;
  1075. }
  1076. if (ampm=="pm" && intHour < 12) {
  1077. intHour += 12;
  1078. }
  1079. if (intMin < 0 || intMin > 59) {
  1080. return "Minute must be between 0 and 59.";
  1081. }
  1082. if (intSec < 0 || intSec > 59) {
  1083. return "Second must be between 0 and 59.";
  1084. }
  1085. return new Date(intYear,intMonth-1,intDay,intHour,intMin,intSec);
  1086. }
  1087. function dateCheck(dateStr,formatStr) {
  1088. var myObj = buildDate(dateStr,formatStr);
  1089. if (typeof myObj == "object") {
  1090.  
  1091. // We got a Date object, so good.
  1092.  
  1093. return true;
  1094. }
  1095. else {
  1096.  
  1097. // We got an error string.
  1098.  
  1099. alert(myObj);
  1100. return false;
  1101.    }
  1102. }
  1103. //  End -->
  1104. </script>
  1105.  
  1106. </HEAD>
  1107.  
  1108. <!-- STEP TWO: Add code into BODY section of document  -->
  1109.  
  1110. <BODY>
  1111.  
  1112. <form>
  1113. <input type=text name="email">
  1114. <input type=button value="Check it!" onClick="if(dateCheck(this.form.email.value,'%mm/%dd/%y')) {alert('Valid Date!')}">
  1115. </form>
  1116.  
  1117.  
  1118. <!-- END OF SCRIPT -->
  1119. <!/PREVIEW>
  1120.  
  1121. <!RELATED>NONE<!/RELATED>
  1122.